<template>
    <div class="sea-uploader">
        <div class="sea-uploader-item" v-for="(item, idx) in modelValue">
            <img v-if="item.type === 'image'" :src="item.src" @click="props.uploader.download(item)"
                 alt="Image preview" class="sea-uploader-image"/>

            <div v-if="item.type !== 'image'" class="sea-uploader-file" @click="props.uploader.download(item)">
                <div><i :class="item.icon"></i></div>
                <span class="sea-filename">{{ item.name }}</span>
            </div>

            <div v-if="!props.readonly" class="sea-uploader-remove" title="删除" @click="removeFile(idx)">
                <i class="fa fa-times-circle-o"></i>
            </div>
        </div>

        <div v-if="!props.readonly" class="sea-uploader-item" v-show="canUpload" v-loading="loading">
            <div @click="$refs.input.click()" class="sea-uploader-button">
                <i class="fa fa-fw fa-plus"></i>
                <input type="file" ref="input" style="display: none" @change="onchange" multiple>
            </div>
        </div>
    </div>

</template>

<script setup lang="ts">
import {ref, watch, computed} from 'vue';
import Layers from "@/lang/layers/index";
import FileUtils from "@/lang/commons/file-utils";
import Uploader, {FileItemType, UploaderType} from "@/widget/uploader";

/**
 * 标准文件上传组件
 *
 * 这不是一个标准组件，不过已经包含核心设计，只需要调整业务逻辑即可
 *
 * 期望用法：复制当前源代码，使之更符合 form 的使用习惯
 */
interface SeaFileUploader {
    // v-model 双向绑定字段
    modelValue?: FileItemType[];
    // 文件大小限制，单位 kb
    maxSize?: number;
    // 文件数量限制
    limit?: number;

    // 只读
    readonly?: boolean;
    // 上传配置
    uploader?: UploaderType;
}

const props = withDefaults(defineProps<SeaFileUploader>(), {
    modelValue: [], limit: 1, maxSize: 1048576, readonly: false, uploader: new Uploader()
});

// Emits
const emit = defineEmits(['update:modelValue', 'change']);

const loading = ref(false);

/**
 * 格式化入参
 *
 * 业务上不需要处理那么多的参数，写好关键参数，剩下的由当前函数统一处理
 *
 * @param list {[FileItemType]} - 文件列表
 */
const wrapFileItemType = (list) => {
    for (let i = 0, len = list.length; i < len; i++) {
        const item = list[i];
        const ext = FileUtils.getExtension(item.name);
        item.icon = FileUtils.getFileIcon(ext);
        item.type = FileUtils.getFileType(ext);
        list.push(item);
    }
}

watch(() => props.modelValue, (val) => {
    wrapFileItemType(val);
});

// 是否可上传文件
const canUpload = computed(() => {
    return Layers.isEmpty(props.valueModel);
});

/**
 * 文件上传
 *
 * @param event -
 */
const onchange = async (event) => {
    if (loading.value === true) {
        Layers.warn('点击过快');
        return;
    }

    try {
        loading.value = true;
        const max = props.maxSize;

        // 单线程逐个上传文件
        const files = event.target.files;
        let ret = Layers.sequentialPromise((resolve, i) => {
            const file = files[i];
            if (file.length > max) {
                const fn = file.name;
                Layers.warn(`最大限制：${max}kb，${fn} 文件过大！`);
                resolve();
            } else {
                props.uploader.upload(file).then(item => {
                    props.modelValue.push(item);
                    resolve();
                }).catch(err => {
                    console.error(err);
                })
            }
        }, 0, files.length);

        ret.then(() => {
            emit('change', props.modelValue);

            // 完成所有文件的提交，关闭加载条
            loading.value = false
            event.target.value = '';
        });
    } catch (e) {
        console.error(e);
        // 触发异常，关闭加载条
        loading.value = false
        event.target.value = '';
    }
};

/**
 * remove image
 */
const removeFile = (idx) => {
    let list = props.modelValue;
    let item = list[idx];
    list.splice(idx, 1);
    emit('change', list);

    // 如果是实时上传的，这里需要完成文件删除逻辑
    props.uploader.remove(item);
};

// 首次载入初始化
{
    // 直接引用参数列表，如果数据发生变化，会直接改动数组内容
    wrapFileItemType(props.modelValue);
}

</script>

<style scoped>
/* 你可以在这里添加你的样式 */
</style>